package com.aaa.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.*;

public class DBUtil {
    //java基础中的方法！
    //1.加载驱动
    //2.获取连接
    //3.sql预编译
    //4.执行sql
    //5.关闭连接，释放资源

    /*private static String URL = "jdbc:mysql://localhost:3306/qy137?useUnicode=true&characterEncoding=utf8&useSSL=false";
    private static String USER = "root";
    private static String PASSWORD = "";
    private static String DRIVER = "com.mysql.jdbc.Driver";*/

    /*private static String URL;
    private static String USER;
    private static String PASSWORD;
    private static String DRIVER;*/
    /*static
    *   静态的：修饰词
    *   修饰变量===》静态变量
    *   修饰方法===》静态方法
    *       加载一次，使用多次
    *   修饰代码块==》静态代码块
    *       加载一次，使用一次。
    *   调用：
    *       非静态可以调用静态的，静态的不能调用非静态的。
    *       静态的都是属于类的。
    *   一般来说工具类的方法都是使用 static 修饰的方法，方便调用！
    *   类名.属性/方法名
    * */
    private static DataSource dataSource;
    //1.加载驱动
    static {

        /**
         *      /druid.properties  表示绝对路径
         *      java项目中是以 src为根目录
         */

        try {
            //1.读取db.properties配置文件 得到一个InputStream
            InputStream asStream = DBUtil.class.getResourceAsStream("/druid.properties");
            //System.out.println(asStream);
            //2.创建一个properties文件对象 用来解析 InputStream字节流
            Properties p = new Properties();
            //3.将流加载到Properties对象中
            p.load(asStream);
            //4.创建数据库连接池(23中设计模式中 工厂模式)
            dataSource = DruidDataSourceFactory.createDataSource(p);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //2.获取连接对象 Connection
    /**
     * 方法的三要素：从我们的需求分析而来！
     *      返回值，方法名，参数列表
     */
    public static Connection getConn(){
        try {
            //直接从连接池中取链接对象
             return dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }


    //3.直接封装一个通用的增删改方法
    /**
     * 方法的三要素
     *     返回值:
     *        方法执行完毕后我们要看到的一定是方法执行是否成功？
     *              可以使用boolean表示 true/false====》 也可以使用-1 失败/1 成功 表示
     *     方法名:update
     *     参数列表:
     *         你要从之前写的单个的增删改查的代码中去思考！
     *              分析发现：
     *                  每一次执行的sql语句不固定，所以需要把sql语句当成参数传递进来！
     *                  预编译：向sql中设置的参数不缺定（个数不确定，类型不确定，顺序不确定）；
     *                      所以可以使用数组作为参数。分析发现，使用Object[]数组最合适
     */
    /**
     *
     * @param sql
     * @param objects 此时写的是数组，数组可以用别的来代替，不定个数的参数。
     * @return
     * Object...  就表示不定个数的参数
     * 使用Object... 来代替 Object[]
     */
    public static int update(String sql, Object... objects){
        //调用本类中获取连接的方法
        Connection conn = getConn();
        PreparedStatement ps = null;
        try {
            //sql预编译
            ps = conn.prepareStatement(sql);
            //向sql语句中设置参数
            //必须去考虑隐含的异常！
            //设置参数
            setParamter(ps,objects);
            //执行sql,返回影响的行数
            return ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            closeAll(conn,ps,null);
        }
        return -1;
    }


    /**
     * 封装通用的设置参数的方法
     * @param ps
     * @param objects
     */
    public static void setParamter(PreparedStatement ps,Object... objects){
        //当长度大于0才证明数组中有数据，此时才需要设置参数
        try {
            if(objects.length > 0){//可以防止下标越界异常
                for (int i = 0; i < objects.length; i++) {
                    ps.setObject(i+1,objects[i]);
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 封装通用的数据转储方法  List<List></>
     * @param sql
     * @param objects
     * @return
     */
    public static List<List> queryList(String sql, Object... objects){
        Connection conn = getConn();
        PreparedStatement ps=null;
        ResultSet resultSet = null;
        try {
            ps = conn.prepareStatement(sql);
            setParamter(ps,objects);
            resultSet = ps.executeQuery();
            ArrayList<List> bigList = new ArrayList<>();
            while (resultSet.next()){
                ArrayList<Object> smList = new ArrayList<>();
                for (int i = 1; i <= resultSet.getMetaData().getColumnCount() ; i++) {
                    smList.add(resultSet.getObject(i));
                }
                bigList.add(smList);
            }
            return bigList;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            closeAll(conn,ps,resultSet);
        }
        return null;
    }

    /**
     * 封装通用的转储List<Map>数据结构
     * @param sql
     * @param objects
     * @return
     */
    public static List<Map> queryListMap(String sql, Object... objects){
        //2.获取连接对象
        Connection conn = getConn();
        //3.sql预编译

        PreparedStatement ps = null;
        ResultSet set = null;
        try {
            ps = conn.prepareStatement(sql);
            //3.1 向sql语句中设置参数
            setParamter(ps,objects);

            //4.执行sql
            set = ps.executeQuery();
            ArrayList<Map> bigList = new ArrayList<>();
            while (set.next()){
                HashMap<Object, Object> map = new HashMap<>();
                for (int i = 1; i <= set.getMetaData().getColumnCount(); i++) {
                    map.put(set.getMetaData().getColumnName(i),set.getObject(i));
                }
                bigList.add(map);
            }
            return bigList;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //5.关闭资源
            closeAll(conn,ps,set);
        }
        return null;
    }


    /**
     * 封装通用的关闭方法
     * @param conn
     * @param ps
     * @param set
     */
    public static void closeAll(Connection conn,PreparedStatement ps,ResultSet set){
        try {
            if(set!=null){
                set.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        try {
            if(ps!=null){
                ps.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        try {
            if(conn!=null){
                conn.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //封装获取主键的方法  int  只有新增一条数据才有主键
    /**
     * 返回值  int
     * 方法名
     * 参数列表
     */
    /**
     * 封装获取主键的方法（插入方法）
     * @param sql
     * @param objects
     * @return
     */
    public static int getPrimaryKey(String sql, Object... objects){
        //调用本类中获取连接的方法
        Connection conn = getConn();
        PreparedStatement ps = null;
        try {
            //sql预编译
            //Statement.RETURN_GENERATED_KEYS 加上这个参数就可以获取主键了
            ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
            //向sql语句中设置参数
            //设置参数
            setParamter(ps,objects);
            //执行sql
            ps.executeUpdate();

            //获取主键
            ResultSet keys = ps.getGeneratedKeys();
            if(keys.next()){
                //返回主键
                return keys.getInt(1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            closeAll(conn,ps,null);
        }
        return -1;
    }














}
